package org.yasukusury.crawler.helper;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.yasukusury.crawler.dao.Copy;
import org.yasukusury.crawler.dao.List;
import org.yasukusury.crawler.mapper.CopyMapper;
import org.yasukusury.crawler.mapper.ListMapper;

import java.io.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.*;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: Y
 * Date: 2017-11-12
 * Time: 19:52
 */
public class BatchManager {

    private ThreadPoolExecutor tpe;
    private ConcurrentHashMap<Future<Result>, String> futureMap;
    public static final String resource = "mybatis-config.xml";
    private SqlSessionFactory sessionFactory;

    public BatchManager() {
        futureMap = new ConcurrentHashMap<>();
    }

    public void start(java.util.List<String> asins) throws IOException {

        InputStream inputStream = Resources.getResourceAsStream(resource);
        sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        ExecutorService service = Executors.newCachedThreadPool();
        for (String asin : asins) {
            PageDistributor distributor = new PageDistributor(asin);
            Future<Result> future = service.submit(distributor);
            System.out.println(String.format("ASIN : %s creeping", asin));
            futureMap.put(future, asin);
        }
        System.out.println("distribution finished");
        try {
            TaskMonitor monitor = new TaskMonitor();
            monitor.watch();
            System.out.println("monitor finished");
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println("monitor starting failed");
        }
    }

    private class TaskMonitor {

        private SqlSession session;
        private CopyMapper copyMapper;
        private ListMapper listMapper;

        private void watch() throws InterruptedException {
            Iterator<Map.Entry<Future<Result>, String>> iterator;
            while (futureMap.size() > 0) {
                iterator = futureMap.entrySet().iterator();
                while (iterator.hasNext()) {
                    Map.Entry<Future<Result>, String> i = iterator.next();
                    if (i.getKey().isDone()) {
                        session = sessionFactory.openSession(false);
                        copyMapper = session.getMapper(CopyMapper.class);
                        listMapper = session.getMapper(ListMapper.class);
                        try {
                            fill(i, copyMapper, listMapper);
                            session.commit();
                            System.out.println("one session committed");
                        } catch (ExecutionException e) {
                            e.printStackTrace();
                            System.out.println("one session rollback");
                            session.rollback();
                        } catch (IllegalArgumentException e) {
                            System.out.println(String.format("ASIN : %s thread exceptly interrupt", i.getValue()));
                            session.rollback();
                        } catch (Exception e) {
                            e.printStackTrace();
                            System.out.println(String.format("ASIN : %s sql error", i.getValue()));
                            session.rollback();
                        }
                        session.close();
                        iterator.remove();
                    }
                    Thread.sleep(100);
                }
            }
        }

        private void fill(Map.Entry<Future<Result>, String> item, CopyMapper copyMapper,
                          ListMapper listMapper) throws ExecutionException, InterruptedException {
            if (item.getKey().get() == null)
                throw new IllegalArgumentException();
            String asin = item.getValue();
            List one = listMapper.select(asin);
            List list = item.getKey().get().getList();
            ConcurrentLinkedDeque<Copy> copies = item.getKey().get().getCopies();
            if (one == null) {
                listMapper.insert(list);
            } else {
                listMapper.update(list);
            }
            listMapper.drop(asin);
            listMapper.create(asin);
            ArrayList<Copy> copies2 = new ArrayList<>(copies);
            if (copies.size() > 0)
                copyMapper.inserts(asin, copies2);
        }

    }
}
